﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using FarPoint.Web.Spread;
using FarPoint.Web.Spread.Chart;
using FarPoint.Web.Spread.Model;
using FarPoint.Web.Chart;
using System.Drawing;
using System.Collections;
using System.Runtime.Serialization;
using System.Xml;
using System.Globalization;
using FarPoint.CalcEngine;
using System.ComponentModel;

namespace ControlExplorer.samples.Sparkline
{
  [Serializable]
  public class BulletChartSparkline : ISparkline
  {
    // Fields
    private static List<WeakReference> __ENCList = new List<WeakReference>();
    private ISegmentData mBad;
    private int mColumn;
    private ISegmentData mForecast;
    private ISegmentData mGood;
    private ISegmentData mMax;
    private ISegmentData mMeasure;
    private int mRow;
    private SheetView mSheetView;
    private ISegmentData mTarget;

    // Events
    public event PropertyTrackChangedEventHandler PropertyChanged;

    public event EventHandler Updated;

    // Methods
    public BulletChartSparkline()
    {
      List<WeakReference> VBt_refL0 = __ENCList;
      lock (VBt_refL0)
      {
        __ENCList.Add(new WeakReference(this));
      }
    }

    protected BulletChartSparkline(SerializationInfo info, StreamingContext context)
    {
      List<WeakReference> VBt_refL0 = __ENCList;
      lock (VBt_refL0)
      {
        __ENCList.Add(new WeakReference(this));
      }
      this.mRow = info.GetInt32("row");
      this.mColumn = info.GetInt32("column");
      this.mMeasure = (ISegmentData)info.GetValue("measure", typeof(ISegmentData));
      this.mTarget = (ISegmentData)info.GetValue("target", typeof(ISegmentData));
      this.mMax = (ISegmentData)info.GetValue("max", typeof(ISegmentData));
      this.mGood = (ISegmentData)info.GetValue("good", typeof(ISegmentData));
      this.mBad = (ISegmentData)info.GetValue("bad", typeof(ISegmentData));
      this.mForecast = (ISegmentData)info.GetValue("forecast", typeof(ISegmentData));
    }

    public BulletChartSparkline(SheetView sheetView, int row, int column, SheetCellRange measure, SheetCellRange target, SheetCellRange max, SheetCellRange good, SheetCellRange bad, SheetCellRange forecast)
    {
      List<WeakReference> VBt_refL0 = __ENCList;
      lock (VBt_refL0)
      {
        __ENCList.Add(new WeakReference(this));
      }
      this.mSheetView = sheetView;
      this.mRow = row;
      this.mColumn = column;
      this.mMeasure = new CellRangeSegmentData(measure.SheetView, measure.Row, measure.Column, measure.RowCount, measure.ColumnCount, new ChartDataSetting());
      this.mTarget = new CellRangeSegmentData(target.SheetView, target.Row, target.Column, target.RowCount, target.ColumnCount, new ChartDataSetting());
      this.mMax = new CellRangeSegmentData(max.SheetView, max.Row, max.Column, max.RowCount, max.ColumnCount, new ChartDataSetting());
      this.mGood = new CellRangeSegmentData(good.SheetView, good.Row, good.Column, good.RowCount, good.ColumnCount, new ChartDataSetting());
      this.mBad = new CellRangeSegmentData(bad.SheetView, bad.Row, bad.Column, bad.RowCount, bad.ColumnCount, new ChartDataSetting());
      this.mForecast = new CellRangeSegmentData(forecast.SheetView, forecast.Row, forecast.Column, forecast.RowCount, forecast.ColumnCount, new ChartDataSetting());
      this.mMeasure.DataChanged += new SegmentDataChangedEventHandler(this.segmentData_DataChanged);
      this.mTarget.DataChanged += new SegmentDataChangedEventHandler(this.segmentData_DataChanged);
      this.mMax.DataChanged += new SegmentDataChangedEventHandler(this.segmentData_DataChanged);
      this.mGood.DataChanged += new SegmentDataChangedEventHandler(this.segmentData_DataChanged);
      this.mBad.DataChanged += new SegmentDataChangedEventHandler(this.segmentData_DataChanged);
      this.mForecast.DataChanged += new SegmentDataChangedEventHandler(this.segmentData_DataChanged);
    }

    public object Clone()
    {
      return new BulletChartSparkline(this.mSheetView, this.mRow, this.mColumn, (SheetCellRange)this.mMeasure, (SheetCellRange)this.mTarget, (SheetCellRange)this.mMax, (SheetCellRange)this.mGood, (SheetCellRange)this.mBad, (SheetCellRange)this.mForecast);
    }

    public bool Deserialize(XmlNodeReader r)
    {
      if (!r.MoveToAttribute("row"))
        return false;
      Row = XmlConvert.ToInt32(r.Value);
      if (!r.MoveToAttribute("column"))
        return false;
      Column = XmlConvert.ToInt32(r.Value);

      if (!r.MoveToAttribute("measureFormula"))
        return false;
      measureFormula = r.Value;
      if (!r.MoveToAttribute("targetFormula"))
        return false;
      targetFormula = r.Value;
      if (!r.MoveToAttribute("maxFormula"))
        return false;
      maxFormula = r.Value;
      if (!r.MoveToAttribute("goodFormula"))
        return false;
      goodFormula = r.Value;
      if (!r.MoveToAttribute("badFormula"))
        return false;
      badFormula = r.Value;
      if (!r.MoveToAttribute("forecastFormula"))
        return false;
      forecastFormula = r.Value;
      return true;
    }

    private string measureFormula;
    private string targetFormula;
    private string maxFormula;
    private string goodFormula;
    private string badFormula;
    private string forecastFormula;

    public void ReloadFormula(FpSpread spread, SheetView ownerSheetView)
    {

      ArrayList dataModels = new ArrayList();
      foreach (SheetView sheet in spread.Sheets)
      {
        dataModels.Add(sheet.DataModel);
      }
      if (!string.IsNullOrEmpty(measureFormula))
        mMeasure = ParseFormula(spread, measureFormula, false, null, null, dataModels);
      if (!string.IsNullOrEmpty(maxFormula))
        mMax = ParseFormula(spread, maxFormula, false, null, null, dataModels);
      if (!string.IsNullOrEmpty(badFormula))
        mBad = ParseFormula(spread, badFormula, false, null, null, dataModels);
      if (!string.IsNullOrEmpty(goodFormula))
        mGood = ParseFormula(spread, goodFormula, false, null, null, dataModels);
      if (!string.IsNullOrEmpty(targetFormula))
        mTarget = ParseFormula(spread, targetFormula, false, null, null, dataModels);
      if (!string.IsNullOrEmpty(forecastFormula))
        mForecast = ParseFormula(spread, forecastFormula, false, null, null, dataModels);
    }
    private CellRangeSegmentData ParseFormula(FpSpread spread, string formula, bool useR1C1, Hashtable customFunctions, Hashtable customNames, ArrayList dataModels)
    {
      Parser parser = new Parser();
      Expression ex = parser.ParseChartDataSource(formula, 0, 0, false, customFunctions, customNames, dataModels);
      CellRangeSegmentData data = null;
      SheetView sheet = null;
      if (ex is ExternalRangeExpression)
      {
        ExternalRangeExpression range = (ExternalRangeExpression)ex;
        for (int i = 0; i < dataModels.Count; i++)
        {
          if (dataModels[i] == range.Source)
          {
            sheet = spread.Sheets[i];
            break;
          }
        }
        data = new CellRangeSegmentData(sheet, range.Row, range.Column, range.RowCount, range.ColumnCount,
                                        new ChartDataSetting());
      }
      return data;
    }
    public bool Serialize(XmlTextWriter w)
    {
      try
      {
        w.WriteAttributeString("row", Row.ToString(CultureInfo.InvariantCulture));
        w.WriteAttributeString("column", Column.ToString(CultureInfo.InvariantCulture));
        CellRangeSegmentData max = (CellRangeSegmentData)mMax;
        CellRangeSegmentData bad = (CellRangeSegmentData)mBad;
        CellRangeSegmentData forecast = (CellRangeSegmentData)mForecast;
        CellRangeSegmentData measure = (CellRangeSegmentData)mMeasure;
        CellRangeSegmentData good = (CellRangeSegmentData)mGood;
        CellRangeSegmentData target = (CellRangeSegmentData)mTarget;
        Parser parser = new Parser();

        ExternalRangeExpression range = new ExternalRangeExpression(max.SheetView.DataModel, max.Row, max.Column,
                                                          max.RowCount, max.ColumnCount);
        maxFormula = parser.Unparse(range, 0, 0, false);

        range = new ExternalRangeExpression(bad.SheetView.DataModel, bad.Row, bad.Column, bad.RowCount,
                                            bad.ColumnCount);
        badFormula = parser.Unparse(range, 0, 0, false);

        range = new ExternalRangeExpression(forecast.SheetView.DataModel, forecast.Row, forecast.Column,
                                            forecast.RowCount, forecast.ColumnCount);
        forecastFormula = parser.Unparse(range, 0, 0, false);

        range = new ExternalRangeExpression(measure.SheetView.DataModel, measure.Row, measure.Column, measure.RowCount,
                                            measure.ColumnCount);
        measureFormula = parser.Unparse(range, 0, 0, false);

        range = new ExternalRangeExpression(good.SheetView.DataModel, good.Row, measure.Column, good.RowCount,
                                            good.ColumnCount);
        goodFormula = parser.Unparse(range, 0, 0, false);

        range = new ExternalRangeExpression(target.SheetView.DataModel, target.Row, target.Column, target.RowCount,
                                            target.ColumnCount);
        targetFormula = parser.Unparse(range, 0, 0, false);


        w.WriteAttributeString("measureFormula", measureFormula);
        w.WriteAttributeString("targetFormula", targetFormula);
        w.WriteAttributeString("maxFormula", maxFormula);
        w.WriteAttributeString("goodFormula", goodFormula);
        w.WriteAttributeString("badFormula", badFormula);
        w.WriteAttributeString("forecastFormula", forecastFormula);
      }
      catch
      {
        return false;
      }
      return true;
    }
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
      info.AddValue("row", this.Row);
      info.AddValue("column", this.Column);
      info.AddValue("measure", this.Measure);
      info.AddValue("target", this.Target);
      info.AddValue("max", this.Max);
      info.AddValue("good", this.Good);
      info.AddValue("bad", this.Bad);
      info.AddValue("forecast", this.Forecast);
    }

    public object GetSparkLineData()
    {
      List<ISegmentData> values = new List<ISegmentData>();
      values.Add(this.Measure);
      values.Add(this.Target);
      values.Add(this.Max);
      values.Add(this.Good);
      values.Add(this.Bad);
      values.Add(this.Forecast);
      return values;
    }

    private void segmentData_DataChanged(object sender, SegmentDataChangedEventArgs e)
    {
      EventHandler VBt_refS0 = this.Updated;
      if (VBt_refS0 != null)
      {
        VBt_refS0(this, EventArgs.Empty);
      }
    }



    // Properties

    public int Column
    {
      get
      {
        return this.mColumn;
      }
      set
      {
        int oldValue = this.mColumn;
        this.mColumn = value;
        PropertyTrackChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyTrackChangedEventArgs("Column", oldValue, value));
        }
      }
    }
    [CellRangeSegmentDataSupport("Measure")]
    public ISegmentData Measure
    {
      get
      {
        return this.mMeasure;
      }
      set
      {
        ISegmentData oldValue = this.mMeasure;
        this.mMeasure = value;
        PropertyTrackChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyTrackChangedEventArgs("Measure", oldValue, value));
        }
      }
    }
    [CellRangeSegmentDataSupport("Target")]
    public ISegmentData Target
    {
      get
      {
        return this.mTarget;
      }
      set
      {
        ISegmentData oldValue = this.mTarget;
        this.mTarget = value;
        PropertyTrackChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyTrackChangedEventArgs("Target", oldValue, value));
        }
      }
    }
    [CellRangeSegmentDataSupport("Maxi")]
    public ISegmentData Max
    {
      get
      {
        return this.mMax;
      }
      set
      {
        ISegmentData oldValue = this.mMax;
        this.mMax = value;
        PropertyTrackChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyTrackChangedEventArgs("Max", oldValue, value));
        }
      }
    }
    [CellRangeSegmentDataSupport("Good")]
    public ISegmentData Good
    {
      get
      {
        return this.mGood;
      }
      set
      {
        ISegmentData oldValue = this.mGood;
        this.mGood = value;
        PropertyTrackChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyTrackChangedEventArgs("Good", oldValue, value));
        }
      }
    }
    [CellRangeSegmentDataSupport("Bad")]
    public ISegmentData Bad
    {
      get
      {
        return this.mBad;
      }
      set
      {
        ISegmentData oldValue = this.mBad;
        this.mBad = value;
        PropertyTrackChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyTrackChangedEventArgs("Bad", oldValue, value));
        }
      }
    }
    [CellRangeSegmentDataSupport("Forecast")]
    public ISegmentData Forecast
    {
      get
      {
        return this.mForecast;
      }
      set
      {
        ISegmentData oldValue = this.mForecast;
        this.mForecast = value;
        PropertyTrackChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyTrackChangedEventArgs("Forecast", oldValue, value));
        }
      }
    }

    public int Row
    {
      get
      {
        return this.mRow;
      }
      set
      {
        int oldValue = this.mRow;
        this.mRow = value;
        PropertyTrackChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyTrackChangedEventArgs("Row", oldValue, value));
        }
      }
    }


  }
  [Serializable]
  public class BulletChartSparklineGroup : ISparklineGroup, ISheetCellRangeSupport
  {
    // Fields
    private static List<WeakReference> __ENCList = new List<WeakReference>();
    private List<ISparkline> mList;
    private BulletChartSparklineRenderer mRenderer;
    private BulletChartSparklineSetting mSetting;

    // Events
    public event EventHandler ClearItems;

    public event ItemEventHandler ItemAdded;

    public event ItemEventHandler ItemRemoved;

    public event ItemEventHandler ItemUpdated;

    public event EventHandler Updated;

    // Methods
    public BulletChartSparklineGroup()
    {
      List<WeakReference> VBt_refL0 = __ENCList;
      lock (VBt_refL0)
      {
        __ENCList.Add(new WeakReference(this));
      }
      this.mRenderer = new BulletChartSparklineRenderer();
      this.mSetting = new BulletChartSparklineSetting();
      this.mList = new List<ISparkline>();
    }

    protected BulletChartSparklineGroup(SerializationInfo info, StreamingContext context)
    {
      List<WeakReference> VBt_refL0 = __ENCList;
      lock (VBt_refL0)
      {
        __ENCList.Add(new WeakReference(this));
      }
      this.mRenderer = new BulletChartSparklineRenderer();
      this.mSetting = new BulletChartSparklineSetting();
      this.mList = new List<ISparkline>();
      this.mRenderer = (BulletChartSparklineRenderer)info.GetValue("renderer", typeof(BulletChartSparklineRenderer));
      this.mSetting = (BulletChartSparklineSetting)info.GetValue("setting", typeof(BulletChartSparklineSetting));
      this.mList = (List<ISparkline>)info.GetValue("list", typeof(List<BulletChartSparkline>));
    }

    protected BulletChartSparklineGroup(BulletChartSparklineRenderer renderer, BulletChartSparklineSetting setting, List<ISparkline> list)
    {
      List<WeakReference> VBt_refL0 = __ENCList;
      lock (VBt_refL0)
      {
        __ENCList.Add(new WeakReference(this));
      }
      this.mRenderer = new BulletChartSparklineRenderer();
      this.mSetting = new BulletChartSparklineSetting();
      this.mList = new List<ISparkline>();
      this.mRenderer = renderer;
      this.mSetting = setting;
      this.mList.AddRange(list);
    }

    public void Add(ISparkline item)
    {
      this.mList.Add(item);
      item.Updated += new EventHandler(this.sparkline_Updated);
      ItemEventHandler VBt_refS0 = this.ItemAdded;
      if (VBt_refS0 != null)
      {
        VBt_refS0(this, new ItemEventArgs(item));
      }
    }

    public void Clear()
    {
      this.mList.Clear();
      EventHandler VBt_refS0 = this.ClearItems;
      if (VBt_refS0 != null)
      {
        VBt_refS0(this, EventArgs.Empty);
      }
    }

    public object Clone()
    {
      return this.CloneSetting();
    }

    public ISparklineGroup CloneSetting()
    {
      return new BulletChartSparklineGroup(this.mRenderer, this.mSetting, this.mList);
    }

    public bool Contains(ISparkline item)
    {
      return this.mList.Contains(item);
    }

    public void CopyTo(ISparkline[] array, int arrayIndex)
    {
      this.mList.CopyTo(array, arrayIndex);
    }

    public bool Deserialize(XmlNodeReader r)
    {
      bool readNext = true;
      bool inRenderer = false;
      bool inSetting = false;
      bool inSparklines = false;
      this.mList = new List<ISparkline>();
      object o;
      if (r.IsEmptyElement)
        return true;
      while (true)
      {
        if (readNext)
        {
          if (!r.Read())
            break;
        }
        else
          readNext = true;
        switch (r.NodeType)
        {
          case XmlNodeType.Element:
            if (r.Name.Equals("renderer"))
            {
              o = Serializer.CreateObjectInstanceAndDeserialize(r, typeof(BulletChartSparklineRenderer));
              if (o != null)
                mRenderer = (BulletChartSparklineRenderer)o;
              readNext = false;
            }
            else if (r.Name.Equals("setting"))
            {
              o = Serializer.CreateObjectInstanceAndDeserialize(r, typeof(BulletChartSparklineSetting));
              if (o != null)
                Setting = (BulletChartSparklineSetting)o;
              readNext = false;
            }
            else if (r.Name.Equals("sparkline"))
            {
              o = Serializer.CreateObjectInstanceAndDeserialize(r, typeof(BulletChartSparkline));
              if (o != null)
                this.Add((BulletChartSparkline)o);
              readNext = false;
            }
            break;
        }
      }
      return true;
    }

    public IEnumerator<ISparkline> GetEnumerator()
    {
      return (IEnumerator<ISparkline>)this.mList.GetEnumerator();
    }

    public IEnumerator GetEnumerator1()
    {
      return this.GetEnumerator();
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
      info.AddValue("renderer", this.mRenderer);
      info.AddValue("setting", this.mSetting);
      info.AddValue("list", this.mList);
    }

    public bool Remove(ISparkline item)
    {
      bool ret = this.mList.Remove(item);
      ItemEventHandler VBt_refS0 = this.ItemRemoved;
      if (VBt_refS0 != null)
      {
        VBt_refS0(this, new ItemEventArgs(item));
      }
      item.Updated -= new EventHandler(this.sparkline_Updated);
      return ret;
    }

    public bool Serialize(XmlTextWriter w)
    {
      w.WriteStartElement("renderer");
      w.WriteAttributeString("class", mRenderer.GetType().FullName);
      w.WriteAttributeString("assembly", mRenderer.GetType().Assembly.FullName);
      mRenderer.Serialize(w);
      w.WriteEndElement();

      w.WriteStartElement("setting");
      w.WriteAttributeString("class", mSetting.GetType().FullName);
      w.WriteAttributeString("assembly", mSetting.GetType().Assembly.FullName);
      mSetting.Serialize(w);
      w.WriteEndElement();
      int count = mList.Count;
      for (int i = 0; i < count; i++)
      {
        w.WriteStartElement("sparkline");
        w.WriteAttributeString("class", mList[i].GetType().FullName);
        w.WriteAttributeString("assembly", mList[i].GetType().Assembly.FullName);
        mList[i].Serialize(w);
        w.WriteEndElement();
      }
      return true;
    }

    private void sparkline_Updated(object sender, EventArgs e)
    {
      EventHandler VBt_refS0 = this.Updated;
      if (VBt_refS0 != null)
      {
        VBt_refS0(this, EventArgs.Empty);
      }
    }

    // Properties
    public int Count
    {
      get
      {
        return this.mList.Count;
      }
    }

    public ISparkline this[int index]
    {
      get
      {
        if ((index < 0) || (index >= this.mList.Count))
        {
          string[] VBt_arrayS0 = new string[] { "Invalid index: ", index.ToString(), " (must be between 0 and ", (this.mList.Count - 1).ToString(), ")" };
          throw new IndexOutOfRangeException(string.Concat(VBt_arrayS0));
        }
        return this.mList[index];
      }
    }

    public ISparklineRenderer Renderer
    {
      get
      {
        return this.mRenderer;
      }
      set
      {
        if (!(value is BulletChartSparklineRenderer))
        {
          throw new ArgumentException("Invalid renderer type - must be BullerChartSparklineRenderer");
        }
        this.mRenderer = (BulletChartSparklineRenderer)value;
        EventHandler VBt_refS0 = this.Updated;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, EventArgs.Empty);
        }
      }
    }

    public ISparklineSetting Setting
    {
      get
      {
        return this.mSetting;
      }
      set
      {
        if (!(value is BulletChartSparklineSetting))
        {
          throw new ArgumentException("Invalid setting type - must be BulletChartSparklineSetting");
        }
        this.mSetting = (BulletChartSparklineSetting)value;
        this.mSetting.PropertyChanged += new PropertyChangedEventHandler(mSetting_PropertyChanged);
        FireUpdated();
      }
    }
    void FireUpdated()
    {
      EventHandler updated = this.Updated;
      if (updated != null)
      {
        updated(this, EventArgs.Empty);
      }
    }
    void mSetting_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      FireUpdated();
    }

    public bool IsReadOnly
    {
      get
      {
        return false;
      }
    }

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      return (IEnumerator<ISparkline>)this.mList.GetEnumerator();
    }

    #endregion

    #region ISheetCellRangeSupport Members

    public event ReloadSheetCellRangeEventHandler FormulaChanged;

    public void ReLoadFormula(FpSpread spread, SheetView ownerSheetView)
    {
      foreach (BulletChartSparkline sp in this)
      {
        sp.ReloadFormula(spread, ownerSheetView);
      }
    }

    public bool ReferenceError
    {
      get { return false; }
    }

    #endregion
  }

  [Serializable]
  public class BulletChartSparklineRenderer : ISparklineRenderer, ISerializable
  {
    // Methods
    public BulletChartSparklineRenderer()
    {
    }

    protected BulletChartSparklineRenderer(SerializationInfo info, StreamingContext context)
    {
    }

    private Color Darker(Color color, int delta)
    {
      int r = color.R - delta;
      int g = color.G - delta;
      int b = color.B - delta;
      if (r > 0xff)
      {
        r = 0xff;
      }
      if (r < 0)
      {
        r = 0;
      }
      if (g > 0xff)
      {
        g = 0xff;
      }
      if (g < 0)
      {
        g = 0;
      }
      if (b > 0xff)
      {
        b = 0xff;
      }
      if (b < 0)
      {
        b = 0;
      }
      return Color.FromArgb(r, g, b);
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
    }

    private void PaintHorizontalBulletChart(Graphics g, Rectangle r, Color ColorScheme, double Tickunit, int f, int good, int bad, int m, int t, double max)
    {
      Brush br = new SolidBrush(this.Darker(ColorScheme, -100));
      g.FillRectangle(br, r.X, r.Y, good, r.Height);
      br = new SolidBrush(ColorScheme);
      g.FillRectangle(br, r.X, r.Y, bad, r.Height);
      int lineLength = 5;
      int x = r.X;
      int tu = (int)Math.Round(Tickunit);
      while (x < (r.X + r.Width))
      {
        g.DrawLine(Pens.Black, x, ((r.Y + r.Height) - 1) - lineLength, x, (r.Y + r.Height) - 1);
        x += tu;
      }
      if (m < max)
      {
        g.FillRectangle(Brushes.Black, r.X, (int)Math.Round((double)((r.Y + (((double)r.Height) / 2.0)) - 3.0)), m, 6);
      }
      else
      {
        g.FillRectangle(Brushes.Red, r.X, (int)Math.Round((double)((r.Y + (((double)r.Height) / 2.0)) - 3.0)), m, 6);
      }
      g.FillRectangle(Brushes.Gray, r.X, (int)Math.Round((double)((r.Y + (((double)r.Height) / 2.0)) - 1.0)), f, 2);
      g.FillRectangle(Brushes.Gray, r.X + t, r.Y, 3, r.Height);
    }

    public void PaintSparkline(Graphics g, Rectangle rectangle, FarPoint.Web.Spread.Sparkline sparkline)
    {
      BulletChartSparklineSetting setting = (BulletChartSparklineSetting)sparkline.Setting;
      BulletChartSparkline bulletChart = sparkline.Data as BulletChartSparkline;
      if (setting.Vertical)
      {
        this.PaintVerticalBulletChart(g, rectangle, setting.Color, setting.TickUnit, Convert.ToInt32(bulletChart.Forecast.GetValue(0)), Convert.ToInt32(bulletChart.Good.GetValue(0)), Convert.ToInt32(bulletChart.Bad.GetValue(0)), Convert.ToInt32(bulletChart.Measure.GetValue(0)), Convert.ToInt32(bulletChart.Target.GetValue(0)), Convert.ToInt32(bulletChart.Max.GetValue(0)));
      }
      else
      {
        this.PaintHorizontalBulletChart(g, rectangle, setting.Color, setting.TickUnit, Convert.ToInt32(bulletChart.Forecast.GetValue(0)), Convert.ToInt32(bulletChart.Good.GetValue(0)), Convert.ToInt32(bulletChart.Bad.GetValue(0)), Convert.ToInt32(bulletChart.Measure.GetValue(0)), Convert.ToInt32(bulletChart.Target.GetValue(0)), Convert.ToInt32(bulletChart.Max.GetValue(0)));
      }
    }

    private void PaintVerticalBulletChart(Graphics g, Rectangle r, Color ColorScheme, double Tickunit, int f, int good, int bad, int m, int t, double max)
    {
      Brush br = new SolidBrush(this.Darker(ColorScheme, -100));
      g.FillRectangle(br, r.X, (r.Y + r.Height) - good, r.Width, good);
      br = new SolidBrush(ColorScheme);
      g.FillRectangle(br, r.X, (r.Y + r.Height) - bad, r.Width, bad);
      int lineLength = 5;
      int y = (r.Y + r.Height) - 1;
      int tu = (int)Math.Round(Tickunit);
      while (y > r.Y)
      {
        g.DrawLine(Pens.Black, ((r.X + r.Width) - 1) - lineLength, y, (r.X + r.Width) - 1, y);
        y -= tu;
      }
      if (m < max)
      {
        g.FillRectangle(Brushes.Black, (int)Math.Round((double)((r.X + (((double)r.Width) / 2.0)) - 3.0)), ((r.Y + r.Height) - m) - 1, 6, m);
      }
      else
      {
        g.FillRectangle(Brushes.Red, (int)Math.Round((double)((r.X + (((double)r.Width) / 2.0)) - 3.0)), ((r.Y + r.Height) - m) - 1, 6, m);
      }
      g.FillRectangle(Brushes.Gray, (int)Math.Round((double)((r.X + (((double)r.Width) / 2.0)) - 1.0)), ((r.Y + r.Height) - f) - 1, 2, f);
      g.FillRectangle(Brushes.Gray, r.X, (r.Y + r.Height) - t, r.Width, 3);
    }

    #region ISerializable Members

    public bool Serialize(System.Xml.XmlTextWriter w)
    {
      Serializer.SerializeString("BulletChartSparklineRenderer", "Name", w);
      return true;
    }
    public bool Deserialize(System.Xml.XmlNodeReader r)
    {
      return true;
    }
    #endregion
  }
  [Serializable]
  public class BulletChartSparklineSetting : ISparklineSetting
  {
    // Fields
    private static List<WeakReference> __ENCList = new List<WeakReference>();
    private Color mColor;
    private double mTickUnit;
    private bool mVertical;

    // Events

    public event PropertyChangedEventHandler PropertyChanged;

    // Methods
    public BulletChartSparklineSetting()
    {
      List<WeakReference> VBt_refL0 = __ENCList;
      lock (VBt_refL0)
      {
        __ENCList.Add(new WeakReference(this));
      }
      this.mColor = Color.AliceBlue;
      this.mVertical = false;
      this.mTickUnit = 1.0;
    }

    protected BulletChartSparklineSetting(SerializationInfo info, StreamingContext context)
    {
      List<WeakReference> VBt_refL0 = __ENCList;
      lock (VBt_refL0)
      {
        __ENCList.Add(new WeakReference(this));
      }
      this.mColor = (Color)info.GetValue("color", typeof(Color));
      this.mVertical = info.GetBoolean("vertical");
      this.mTickUnit = info.GetDouble("tickUnit");
    }

    public BulletChartSparklineSetting(Color color, bool vertical, double tickUnit)
    {
      List<WeakReference> VBt_refL0 = __ENCList;
      lock (VBt_refL0)
      {
        __ENCList.Add(new WeakReference(this));
      }
      this.mColor = color;
      this.mVertical = vertical;
      this.mTickUnit = tickUnit;
    }

    public bool Deserialize(XmlNodeReader r)
    {
      ColorConverter con = new ColorConverter();
      if (!r.MoveToAttribute("Color"))
        return false;
      mColor = (Color)con.ConvertFromInvariantString(r.Value);

      if (!r.MoveToAttribute("TickUnit"))
        return false;
      mTickUnit = XmlConvert.ToDouble(r.Value);

      if (!r.MoveToAttribute("Vertical"))
        return false;
      mVertical = XmlConvert.ToBoolean(r.Value);
      return true;
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
      info.AddValue("color", this.Color);
      info.AddValue("vertical", this.Vertical);
      info.AddValue("tickUnit", this.TickUnit);
    }

    public bool Serialize(XmlTextWriter w)
    {
      ColorConverter colorConverter = new ColorConverter();
      w.WriteAttributeString("Color", colorConverter.ConvertToString(this.Color));
      w.WriteAttributeString("TickUnit", this.TickUnit.ToString());
      w.WriteAttributeString("Vertical", XmlConvert.ToString(this.Vertical));
      return true;
    }

    // Properties
    public Color Color
    {
      get
      {
        return this.mColor;
      }
      set
      {
        this.mColor = value;
        PropertyChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyChangedEventArgs("Color"));
        }
      }
    }

    public double TickUnit
    {
      get
      {
        return this.mTickUnit;
      }
      set
      {
        this.mTickUnit = value;
        PropertyChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyChangedEventArgs("TickUnit"));
        }
      }
    }

    public bool Vertical
    {
      get
      {
        return this.mVertical;
      }
      set
      {
        this.mVertical = value;
        PropertyChangedEventHandler VBt_refS0 = this.PropertyChanged;
        if (VBt_refS0 != null)
        {
          VBt_refS0(this, new PropertyChangedEventArgs("Vertical"));
        }
      }
    }
  }

  [AttributeUsage(AttributeTargets.Property)]
  public class CellRangeSegmentDataSupportAttribute : Attribute
  {
    // Fields
    private bool _multiCell;
    private string _text;

    // Methods
    public CellRangeSegmentDataSupportAttribute(string text)
    {
      this._text = text;
    }

    public CellRangeSegmentDataSupportAttribute(string text, bool multi)
      : this(text)
    {
      this._multiCell = multi;
    }

    // Properties
    public bool MultiCell
    {
      get
      {
        return this._multiCell;
      }
    }

    public string Text
    {
      get
      {
        return this._text;
      }
    }
  }
}